package server;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Scanner;

public class Game {
	
	public static final int MAX_NUMBER_OF_PLAYERS = 6;
	public static final int QUESTIONS_PER_GAME = 5;
	public static final long MAX_TIME_PER_QUESTION = 15000; 
	
	// Players indexed by their username. This table holds all connected players
	private static Hashtable<String,Player> playertable = new Hashtable<String,Player>(MAX_NUMBER_OF_PLAYERS+1,1f);
	
	// Holds players currently in a game.
	private static Hashtable<String,Player> activePlayers = new Hashtable<String,Player>(MAX_NUMBER_OF_PLAYERS+1,1f);
	
	private static ArrayList<Question> questionBank = new ArrayList<Question>();
	private static GameThread gameThread = new GameThread(playertable, activePlayers, questionBank);
	
	/**
	 * If the current game is counting down to start, the active players are updated.
	 * Otherwise it starts a new game. If a game is already started, it is stopped beforehand.
	 */
	public static void startNewGame() {
		if (gameThread.isCountingDown()) { 
			/*
			 * If the game is not started, but is simply counting down, just add the new player to the game.
			 */
			activePlayers.putAll(playertable);
		} else {
			/*
			 * The game is either running or not started. Either way we will simply stop it and start a new. 
			 */
			if (gameThread.isGameStarted())
				stopCurrentGame();
			
			Server.consolePrint("Starting new game.");
			activePlayers.putAll(playertable);
			
			gameThread = new GameThread(playertable, activePlayers, questionBank);
			gameThread.start();
		}
	}
	
	/**
	 * Stops the current game regardless of the state.
	 */
	private static void stopCurrentGame() {
		Server.consolePrint("Stopping current game.");
		activePlayers.clear();
		if (gameThread.isAlive())
			gameThread.stopGame();
	}
	
	/**
	 * Returns the game info and the currently connected users. Should only be printed to 
	 * client when a connection is opened.
	 * @return see above.
	 */
	public static String gameInfo() {
		
		String info = "There are "+questionBank.size()+" questions left\n"+
					  "The currently connected users are:\n";
		
		for (String u : playertable.keySet())
			info += u+"\n";
		
		info += "You can choose any username not already taken. Usernames must not contain spaces.\n";
		info += "Type in your username.";
		
		return info;
	}
	
	/**
	 * Makes the current GameThread validate an answer from a player.
	 * @param username username of the player who answered
	 * @param answer message from the client
	 */
	public static void processPlayerAnswer(String username, String answer) {
		Server.consolePrint(username + ": " + answer);
		gameThread.processPlayerAnswer(username, answer);
	}
	
	/**
	 * Checks if a username can be added to the game
	 * @param username Desired username of the player
	 * @return -1: Server full <br/>
	 *    &nbsp;0: Username taken <br/>
	 *    &nbsp;1: Success
	 */
	public static int canAddUser(String username) {
		if (playertable.size() >= 6) // SHOULD never get above 6, but >= just in case something weird happens.
			return -1;
		
		if (playertable.containsKey(username))
			return 0;
		
		return 1;
	}
	
	/**
	 * Tries to add a player to the game. May output something on <code>out</code> before it returns!
	 * @param username Desired username of the player
	 * @param socketThread Will be used to print info to the player
	 * @return -1: Server full <br/>
	 *    &nbsp;0: Username taken <br/>
	 *    &nbsp;1: Success
	 */
	public static int addUser(String username, ServerSocketThread socketThread){
		int returnValue = canAddUser(username);
		if (returnValue > 0) { 
			synchronized (playertable){
				
				playertable.put(username, new Player(username,socketThread));
				
				if (!gameThread.isGameStarted()) {
					if ( playertable.size() > 1 ){
						startNewGame();
						
					} else {
						socketThread.send("Waiting for another player.");
					}
					
				} else {
					/*
					 *  Player should somehow be queued to join the current game.
					 *  
					 */
					socketThread.send("Queued");
				}
			}
		}
		return returnValue;
	}
	
	/**
	 * Removes a user from the game. If there is only one player left, the current game ends.
	 * @param username username of the player who should be removed.
	 */
	public static synchronized void removeUser(String username){
		gameThread.sendToAllPlayers(username + " have disconnected.");
		Server.consolePrint("Removing "+ username);
		playertable.remove(username);
		activePlayers.remove(username);
		
		if (activePlayers.size() < 2){
			stopCurrentGame(); 
			if (playertable.size() > 1)
				startNewGame();
		}
		
	}
	
	/**
	 * Clears the questionBank and reads all questions from the questions.txt
	 */
	public static void readQuestionFile(){
		String fileName = "questions.txt"; // could be done dinamicly, but lets keep it simple for now.
		try {
            File questionFile;
            /* When we execute from the jar file we use: */
            String jarPath = System.getProperty("java.class.path");
            String fileSeparator = System.getProperty("file.separator");
            fileName = jarPath.substring(0,jarPath.lastIndexOf(fileSeparator)+1) + "questions.txt";
            questionFile = new File(fileName);

            /* Under development we use these lines */
            if (!questionFile.exists()) {
            	String errorMessage = "No questionfile found. Please create a questions.txt file with questions at this path: \"" + 
            						  fileName+"\"";
            	Server.consolePrint(errorMessage);
            	System.exit(0);
            }

            Server.consolePrint("Loading questionfile:"+questionFile.getAbsolutePath());
            Scanner fileScanner = new Scanner(questionFile);
            String question;
            String correctAnswer;
            String[] answers;
            questionBank.clear();
            int lineNumber = 0;
            try {
	            while (fileScanner.hasNextLine()) {
	
	        		/*
	        		 * Correct format of the file is:
	        		 * A line containing the question
	        		 * A line containing the correct answer
	        		 * A line with all answers, separated by | (pipe). The first letter of each answer should 
	        		 * be unique to the question, as it will be used as the identifier.
	        		 */
	            	question = fileScanner.nextLine();
	            	lineNumber++;
	            	
	            	if (fileScanner.hasNextLine()) {
	            		
	            		correctAnswer = fileScanner.nextLine();
	            		lineNumber++;
	            		
	            		if (fileScanner.hasNextLine()) {
	            			answers = fileScanner.nextLine().split("\\|"); // | in regexes means OR, which is why we escape it.
	            			lineNumber++;
	            			
	            			boolean correctAnswerFound = false;
	            			for (String a : answers){
	            				if (a.endsWith(correctAnswer))
	            					correctAnswerFound = true;
	            			}
	            				
	            			
	            			if (correctAnswerFound) {
	            				questionBank.add(new Question(question,answers,correctAnswer));
	            			} else {
	            				String errorMessage = "Question \""+question+"\" dropped, because the \"correct"+
	            									  " answer\" were not of in the answers";
							  	Server.consolePrint(errorMessage);
	            			} // end if (correctAnswer < answers.length)
	            			
	            		} else {
	            			throw new MalformedQuestionFile("Incomplete question at the end of questionfile");
	            		} // end if (fileScanner.hasNextLine())
	            		
	            	} else {
	            		throw new MalformedQuestionFile("Incomplete question at the end of questionfile");
	        		} // end if (fileScanner.hasNextLine())
	                
	            } // end while (fileScanner.hasNextLine())
	            
	            // Randomize the questions
	            Collections.shuffle(questionBank);
	            
            } catch (MalformedQuestionFile ex){
            	
            	String errorMessage = "Reading of questionfile terminated on line " +lineNumber +":\n"+ex.getMessage();
            	
            	Server.consolePrint(errorMessage);
            }
            
        } catch (FileNotFoundException ex) {
            Server.consolePrint(fileName + " not found.");
        }
	}
	
}
